#ifndef __STEGIT_MATH_H__
#define __STEGIT_MATH_H__ 1

// include complex before fftw3.h: force fftw_complex to be compatible to std::complex
#include <complex>
#include <valarray>

#include <fftw3.h>
#include <stdint.h>

/**
 * @brief mathematical functionality for StegIT-2
 * @author mnutzinger
 *
 * This class contains only static member functions and cannot be instantiated.
 */
class CStegitMath
{
public:
	enum EInterpolationType
	{
		INTERP_TYPE_LINEAR = 0,
		INTERP_TYPE_POLYNOMIAL,
		INTERP_TYPE_CSPLINE,
		INTERP_TYPE_CSPLINE_PERIODIC,
		INTERP_TYPE_AKIMA,
		INTERP_TYPE_AKIMA_PERIODIC,
	};
	static double pow(double pBase, int pPower);
	static std::complex<double> pow(double pBase, std::complex<double> pPower);
	static double log(double pValue);

	// convert samples between different types, important for the embedding
	// engines

	static void convert(double* pInput, std::complex<double>* pOutput,
			unsigned int pLength);
	static void convert(std::complex<double>* pInput, double* pOutput,
			unsigned int pLength);
	static void convert(std::complex<double>* pInput, fftw_complex* pOutput,
			unsigned int pLength);
	static void convert(double* pInput, fftw_complex* pOutput,
			unsigned int pLength);
	static void convert(fftw_complex* pInput, std::complex<double>* pOutput,
			unsigned int pLength);
	static void convert(fftw_complex* pInput, double* pOutput,
			unsigned int pLength);
	static void convert(std::valarray<double>& pInput, fftw_complex* pOutput,
			unsigned int pLength);

	/**
	 * @param pOut Output buffer for interpolated samples.
	 * @param pOutLen Length of the output buffer(unit: double).
	 * @param pIn Input buffer for input samples.
	 * @param pInLen Length of input buffer (unit: double).
	 * @param pInTmp A temporary buffer which is needed as temporary buffer
	 *        by this methode and must have the same length as pIn. The stored
	 *        values of this buffer will be discard and overwritten.
	 * @param pType Type of interpolation. Some interpolation types needs a
	 *        minimum of required input samples. For example: Cubic interpolation
	 *        requires a minimum of 3 samples.
	 *        For a periodic interpolation type the first and last sample of
	 *        pIn must have the same values.
	 * @return True for successful execution or false for an internal error.
	 */
	static bool interpolate(double* pOut,
			unsigned int pOutLen,
			double* pIn,
			unsigned int pInLen,
			double* pInTmp,
			EInterpolationType pType,
			bool pExtrapolation);

	/**
	 * interpolate function without pInTmp parameter.
	 *
	 * If this methode is used, then this methode will call the function with
	 * pInTmp parameter. The buffer for pInTmp will be dynamically allocated.
	 */
	static bool interpolate(double* pOut,
			unsigned int pOutLen,
			double* pIn,
			unsigned int pInLen,
			EInterpolationType pType,
			bool pExtrapolation);

	/**
	 * @param pOut Output buffer for interpolated samples.
	 * @param pOutLen Length of the output buffer(unit: double).
	 * @param pOutLenTime Time between first and last sample =
	 *        (count - 1) * sampleOutTime
	 * @param pOutOffsetTime Time between first input and first output sample.
	 *        (firstOutTime - firstInTime)
	 * @param pIn Input buffer for input samples.
	 * @param pInLen Length of input buffer (unit: double).
	 * @param pInLenTime Time between first and last sample =
	 *        (count - 1) * sampleInTime
	 * @param pType Type of interpolation. Some interpolation types needs a
	 *        minimum of required input samples. For example: Cubic interpolation
	 *        requires a minimum of 3 samples.
	 *        For a periodic interpolation type the first and last sample of
	 *        pIn must have the same values.
	 * @return True for successful execution or false for an internal error.
	 */
	static bool interpolate(double* pOut,
			unsigned int pOutLen,
			double pOutLenTime,
			double pOutOffsetTime,
			double* pIn,
			unsigned int pInLen,
			double pInLenTime,
			EInterpolationType pType);

	/**
	 * Create a 16 bit CRC
	 */
	static uint16_t inCksum(void *pAddr, int pLen);

	static unsigned char CRC8_BlockChecksum(const void *data, int length);
private:
	CStegitMath();
	CStegitMath(const CStegitMath& );
	CStegitMath& operator=(const CStegitMath& );

	/**
	 * number of rounds for logarithm approximation
	 */
	static const unsigned int sLogApproxRounds;

	static unsigned char sCRCTable[256];

	static void CRC8_InitChecksum(unsigned char &crcvalue);
	static void CRC8_Update(unsigned char &crcvalue, const unsigned char data);
	static void CRC8_UpdateChecksum(unsigned char &crcvalue, const void *data, int length);
	static void CRC8_FinishChecksum(unsigned char &crcvalue);
};

#endif // __STEGIT_MATH_H__

